﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: ByteHelper.cs
* 文件说明: 包括字节顺序的数据与字节数组转换帮助类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 包括字节顺序的数据与字节数组转换帮助类
    /// </summary>
    public static class ByteHelper
    {
        /// <summary>
        /// 获取二进制字符串
        /// </summary>
        /// <param name="value">值</param>
        /// <returns>字符串</returns>
        public static string BitsString(byte value)
        {
            int size = sizeof(byte) * 8;
            return Convert.ToString(value, 2).ToUpper().PadLeft(size, '0');
        }

        /// <summary>
        /// 获取二进制字符串
        /// </summary>
        /// <param name="value">值</param>
        /// <returns>字符串</returns>
        public static string BitsString(ushort value)
        {
            int size = sizeof(ushort) * 8;
            return Convert.ToString(value, 2).ToUpper().PadLeft(size, '0');
        }

        /// <summary>
        /// 获取二进制字符串
        /// </summary>
        /// <param name="value">值</param>
        /// <returns>字符串</returns>
        public static string BitsString(uint value)
        {
            int size = sizeof(uint) * 8;
            return Convert.ToString(value, 2).ToUpper().PadLeft(size, '0');
        }

        /// <summary>
        /// 获取单字节值
        /// </summary>
        /// <param name="value">二进制字符串</param>
        /// <returns>单字节值</returns>
        public static byte BitsByte(string value)
        {
            int size = sizeof(byte) * 8;
            if (value.Length > size) value = value.Substring(value.Length - size, size);
            return Convert.ToByte(value, 2);
        }

        /// <summary>
        /// 获取无符号双字节值
        /// </summary>
        /// <param name="value">二进制字符串</param>
        /// <returns>无符号双字节值</returns>
        public static ushort BitsUShort(string value)
        {
            int size = sizeof(ushort) * 8;
            if (value.Length > size) value = value.Substring(value.Length - size, size);
            return Convert.ToUInt16(value, 2);
        }

        /// <summary>
        /// 获取无符号四字节值
        /// </summary>
        /// <param name="value">二进制字符串</param>
        /// <returns>无符号四字节值</returns>
        public static uint BitsUInt(string value)
        {
            int size = sizeof(uint) * 8;
            if (value.Length > size) value = value.Substring(value.Length - size, size);
            return Convert.ToUInt32(value, 2);
        }

        /// <summary>
        /// 字节数组转十六进制字符串
        /// </summary>
        /// <param name="bytes">字节数组</param>
        /// <param name="span">分隔符</param>
        /// <returns>结果</returns>
        public static string HexString(IEnumerable<byte> bytes, string span = "")
        {
            ValueStringBuilder sb = new();
            foreach (byte b in bytes)
            {
                sb.Append(HexString(b));
                if (!string.IsNullOrEmpty(span)) sb.Append(span);
            }

            return sb.ToString();
        }

        /// <summary>
        /// 十六进制字符串转字节数组
        /// </summary>
        /// <param name="str">十六进制字符串</param>
        /// <param name="span">分隔符</param>
        /// <returns>结果</returns>
        public static byte[] HexBytes(string str, string span = "")
        {
            if (!string.IsNullOrEmpty(span))
            {
                str = str.Replace(span, "");
            }

            if (string.IsNullOrEmpty(str))
            {
                return new byte[0];
            }

            var bytes = new byte[str.Length / 2];
            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = byte.Parse(str.Substring(2 * i, 2), NumberStyles.AllowHexSpecifier);
            }

            return bytes;
        }

        /// <summary>
        /// 字节转十六进制字符串
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public static string HexString(byte b)
        {
            return b.ToString("X2");
        }

        /// <summary>
        /// 返回数据索引
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="start">起始位置</param>
        /// <param name="value">数据</param>
        /// <returns>索引</returns>
        public static int IndexOf<T>(this ReadOnlySpan<T> source, int start, ReadOnlySpan<T> value) where T : IEquatable<T>
        {
            return source.Slice(start).IndexOf(value);
        }

        #region GetBytes
        /// <summary>
        /// Returns the specified Boolean value as a byte array.
        /// </summary>
        /// <param name="value">A Boolean value.</param>
        /// <returns>A byte array with length 1.</returns>
        public static byte[] GetBytesFromBool(bool value)
        {
            byte[] r = new byte[1];
            r[0] = (value ? (byte)1 : (byte)0);
            return r;
        }

        /// <summary>
        /// Returns the specified 16-bit signed integer value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 2.</returns>
        public static byte[] GetBytesFromShort(short value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(short)];
            Unsafe.As<byte, short>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified 32-bit signed integer value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 4.</returns>
        public static byte[] GetBytesFromInt(int value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(int)];
            Unsafe.As<byte, int>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified 64-bit signed integer value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 8.</returns>
        public static byte[] GetBytesFromLong(long value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(long)];
            Unsafe.As<byte, long>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified 16-bit unsigned integer value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 2.</returns>
        public static byte[] GetBytesFromUShort(ushort value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(ushort)];
            Unsafe.As<byte, ushort>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified 32-bit unsigned integer value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 4.</returns>
        public static byte[] GetBytesFromUInt(uint value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(uint)];
            Unsafe.As<byte, uint>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified 64-bit signed integer value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 8.</returns>
        public static byte[] GetBytesFromULong(ulong value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(ulong)];
            Unsafe.As<byte, ulong>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified single-precision floating point value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 4.</returns>
        public static byte[] GetBytesFromFloat(float value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(float)];
            Unsafe.As<byte, float>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        /// <summary>
        /// Returns the specified double-precision floating point value as an array of bytes.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>An array of bytes with length 8.</returns>
        public static byte[] GetBytesFromDouble(double value, bool isLittleEndian = true)
        {
            byte[] bytes = new byte[sizeof(double)];
            Unsafe.As<byte, double>(ref bytes[0]) = value;
            if (!isLittleEndian) bytes.Reverse();
            return bytes;
        }

        #endregion

        #region ToValue

        internal static void ThrowArgumentNullException()
        {
            throw new ArgumentNullException("value不能为空");
        }

        internal static void ThrowArgumentOutOfRangeException()
        {
            throw new ArgumentOutOfRangeException("startIndex必须小于value 长度");
        }

        internal static void ThrowArgumentException()
        {
            throw new ArgumentOutOfRangeException("返回值长度必须小于(startIndex + 返回值长度)");
        }

        /// <summary>
        /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 16-bit signed integer formed by two bytes beginning at <paramref name="startIndex"/>.</returns>
        public static short ToShort(byte[] value, int startIndex, bool isLittleEndian = true)
        {
            return ToShort(value.AsSpan(), startIndex, isLittleEndian);
        }

        /// <summary>
        /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 32-bit signed integer formed by four bytes beginning at <paramref name="startIndex"/>.</returns>
        public static int ToInt(byte[] value, int startIndex, bool isLittleEndian = true)
        {
            return ToInt(value.AsSpan(), startIndex, isLittleEndian);
        }

        /// <summary>
        /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 64-bit signed integer formed by eight bytes beginning at <paramref name="startIndex"/>.</returns>
        public static long ToLong(byte[] value, int startIndex, bool isLittleEndian = true)
        {
            return ToLong(value.AsSpan(), startIndex, isLittleEndian);
        }

        /// <summary>
        /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 16-bit unsigned integer formed by two bytes beginning at <paramref name="startIndex"/>.</returns>
        public static ushort ToUShort(byte[] value, int startIndex, bool isLittleEndian = true) => unchecked((ushort)ToShort(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 32-bit unsigned integer formed by four bytes beginning at <paramref name="startIndex"/>.</returns>
        public static uint ToUInt(byte[] value, int startIndex, bool isLittleEndian = true) => unchecked((uint)ToInt(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a 64-bit unsigned integer converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 64-bit unsigned integer formed by eight bytes beginning at <paramref name="startIndex"/>.</returns>
        public static ulong ToULong(byte[] value, int startIndex, bool isLittleEndian = true) => unchecked((ulong)ToLong(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A single-precision floating point number formed by four bytes beginning at <paramref name="startIndex"/>.</returns>
        public static float ToFloat(byte[] value, int startIndex, bool isLittleEndian = true) => Int32BitsToSingle(ToInt(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a double-precision floating point number converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A double-precision floating point number formed by eight bytes beginning at <paramref name="startIndex"/>.</returns>
        public static double ToDouble(byte[] value, int startIndex, bool isLittleEndian = true) => Int64BitsToDouble(ToLong(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a Boolean value converted from two bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">A byte array.</param>
        /// <param name="startIndex">The index of the byte within <paramref name="value"/>.</param>
        /// <returns><see langword="true"/> if the byte at <paramref name="startIndex"/> is nonzero; otherwise <see langword="false"/>.</returns>
        public static bool ToBoolean(byte[] value, int startIndex)
        {
            if (value == null)
                ThrowArgumentNullException();
            if (startIndex < 0)
                ThrowArgumentOutOfRangeException();
            if (startIndex >= value.Length)
                ThrowArgumentOutOfRangeException(); // differs from other overloads, which throw base ArgumentException

            return value[startIndex] != 0;
        }

        /// <summary>
        /// Converts the specified double-precision floating point number to a 64-bit signed integer.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A 64-bit signed integer whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe long DoubleToInt64Bits(double value) => *((long*)&value);

        /// <summary>
        /// Converts the specified 64-bit signed integer to a double-precision floating point number.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A double-precision floating point number whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe double Int64BitsToDouble(long value) => *((double*)&value);

        /// <summary>
        /// Converts the specified single-precision floating point number to a 32-bit signed integer.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A 32-bit signed integer whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe int SingleToInt32Bits(float value) => *((int*)&value);

        /// <summary>
        /// Converts the specified 32-bit signed integer to a single-precision floating point number.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A single-precision floating point number whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe float Int32BitsToSingle(int value) => *((float*)&value);

        /// <summary>
        /// Converts the specified double-precision floating point number to a 64-bit unsigned integer.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A 64-bit unsigned integer whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe ulong DoubleToUInt64Bits(double value) => *((ulong*)&value);

        /// <summary>
        /// Converts the specified 64-bit unsigned integer to a double-precision floating point number.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A double-precision floating point number whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe double UInt64BitsToDouble(ulong value) => *((double*)&value);

        /// <summary>
        /// Converts the specified single-precision floating point number to a 32-bit unsigned integer.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A 32-bit unsigned integer whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe uint SingleToUInt32Bits(float value) => *((uint*)&value);

        /// <summary>
        /// Converts the specified 32-bit unsigned integer to a single-precision floating point number.
        /// </summary>
        /// <param name="value">The number to convert.</param>
        /// <returns>A single-precision floating point number whose bits are identical to <paramref name="value"/>.</returns>
        internal static unsafe float UInt32BitsToSingle(uint value) => *((float*)&value);

        /// <summary>
        /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 16-bit signed integer formed by two bytes beginning at <paramref name="startIndex"/>.</returns>
        public static short ToShort(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true)
        {
            if (value == null)
                ThrowArgumentNullException();
            if (unchecked((uint)startIndex) >= unchecked((uint)value.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > value.Length - sizeof(short))
                ThrowArgumentException();

            var m_buffer = value.Slice(startIndex, sizeof(short));
            if (isLittleEndian)
                return (short)(m_buffer[0] | m_buffer[1] << 8);
            else
                return (short)(m_buffer[1] | m_buffer[0] << 8);
        }

        /// <summary>
        /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 32-bit signed integer formed by four bytes beginning at <paramref name="startIndex"/>.</returns>
        public static int ToInt(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true)
        {
            if (value == null)
                ThrowArgumentNullException();
            if (unchecked((uint)startIndex) >= unchecked((uint)value.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > value.Length - sizeof(int))
                ThrowArgumentException();

            var m_buffer = value.Slice(startIndex, sizeof(int));
            if (isLittleEndian)
                return (int)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
            else
                return (int)(m_buffer[3] | m_buffer[2] << 8 | m_buffer[1] << 16 | m_buffer[0] << 24);
        }

        /// <summary>
        /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 64-bit signed integer formed by eight bytes beginning at <paramref name="startIndex"/>.</returns>
        public static long ToLong(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true)
        {
            if (value == null)
                ThrowArgumentNullException();
            if (unchecked((uint)startIndex) >= unchecked((uint)value.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > value.Length - sizeof(long))
                ThrowArgumentException();

            uint lo = ToUInt(value, startIndex, isLittleEndian);
            uint hi = ToUInt(value, startIndex + 4, isLittleEndian);

            if (isLittleEndian)
                return (long)((ulong)hi) << 32 | lo;
            else
                return (long)((ulong)lo) << 32 | hi;
        }

        /// <summary>
        /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 16-bit unsigned integer formed by two bytes beginning at <paramref name="startIndex"/>.</returns>
        public static ushort ToUShort(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true) => unchecked((ushort)ToShort(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 32-bit unsigned integer formed by four bytes beginning at <paramref name="startIndex"/>.</returns>
        public static uint ToUInt(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true) => unchecked((uint)ToInt(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a 64-bit unsigned integer converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A 64-bit unsigned integer formed by eight bytes beginning at <paramref name="startIndex"/>.</returns>
        public static ulong ToULong(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true) => unchecked((ulong)ToLong(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A single-precision floating point number formed by four bytes beginning at <paramref name="startIndex"/>.</returns>
        public static float ToFloat(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true) => Int32BitsToSingle(ToInt(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a double-precision floating point number converted from four bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">An array of bytes.</param>
        /// <param name="startIndex">The starting position within <paramref name="value"/>.</param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns>A double-precision floating point number formed by eight bytes beginning at <paramref name="startIndex"/>.</returns>
        public static double ToDouble(ReadOnlySpan<byte> value, int startIndex, bool isLittleEndian = true) => Int64BitsToDouble(ToLong(value, startIndex, isLittleEndian));

        /// <summary>
        /// Returns a Boolean value converted from two bytes at a specified position in a byte array.
        /// </summary>
        /// <param name="value">A byte array.</param>
        /// <param name="startIndex">The index of the byte within <paramref name="value"/>.</param>
        /// <returns><see langword="true"/> if the byte at <paramref name="startIndex"/> is nonzero; otherwise <see langword="false"/>.</returns>
        public static bool ToBoolean(ReadOnlySpan<byte> value, int startIndex)
        {
            if (value == null)
                ThrowArgumentNullException();
            if (startIndex < 0)
                ThrowArgumentOutOfRangeException();
            if (startIndex >= value.Length)
                ThrowArgumentOutOfRangeException(); // differs from other overloads, which throw base ArgumentException

            return value[startIndex] != 0;
        }

        #endregion

        #region TryWrite
        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool TryWriteBool(byte[] buffer, int startIndex, bool value)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(byte))
                ThrowArgumentException();

            GetBytesFromBool(value).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(byte)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool TryWriteByte(byte[] buffer, int startIndex, Byte value)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(byte))
                ThrowArgumentException();

            buffer[startIndex] = value;
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool TryWriteBytes(byte[] buffer, int startIndex, Byte[] value)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - value.Length)
                ThrowArgumentException();

            value.AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, value.Length));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static bool TryWriteBytes(byte[] buffer, int startIndex, Byte[] value, int offset, int length)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (unchecked((uint)offset) >= unchecked((uint)value.Length))
                ThrowArgumentOutOfRangeException();
            if (offset > value.Length - length)
                ThrowArgumentException();
            if (startIndex > buffer.Length - length)
                ThrowArgumentException();

            value.AsSpan().Slice(offset, length).CopyTo(buffer.AsSpan().Slice(startIndex, length));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool TryWriteSpan(byte[] buffer, int startIndex, ReadOnlySpan<byte> value)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - value.Length)
                ThrowArgumentException();

            value.CopyTo(buffer.AsSpan().Slice(startIndex, value.Length));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns></returns>
        public static bool TryWriteShort(byte[] buffer, int startIndex, short value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(short))
                ThrowArgumentException();

            GetBytesFromShort(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(short)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns></returns>
        public static bool TryWriteInt(byte[] buffer, int startIndex, int value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(int))
                ThrowArgumentException();

            GetBytesFromInt(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(int)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian"></param>
        /// <returns></returns>
        public static bool TryWriteLong(byte[] buffer, int startIndex, long value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(long))
                ThrowArgumentException();

            GetBytesFromLong(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(long)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian"></param>
        /// <returns></returns>
        public static bool TryWriteUShort(byte[] buffer, int startIndex, ushort value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(short))
                ThrowArgumentException();

            GetBytesFromUShort(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(ushort)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns></returns>
        public static bool TryWriteUInt(byte[] buffer, int startIndex, uint value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(int))
                ThrowArgumentException();

            GetBytesFromUInt(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(uint)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns></returns>
        public static bool TryWriteULong(byte[] buffer, int startIndex, ulong value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(long))
                ThrowArgumentException();

            GetBytesFromULong(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(ulong)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns></returns>
        public static bool TryWriteFloat(byte[] buffer, int startIndex, float value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(float))
                ThrowArgumentException();

            GetBytesFromFloat(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(float)));
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="startIndex"></param>
        /// <param name="value"></param>
        /// <param name="isLittleEndian">字节顺序，true为小端字节(低字节序)，false为大端字节序(高字节序)</param>
        /// <returns></returns>
        public static bool TryWriteDouble(byte[] buffer, int startIndex, double value, bool isLittleEndian = true)
        {
            if (unchecked((uint)startIndex) >= unchecked((uint)buffer.Length))
                ThrowArgumentOutOfRangeException();
            if (startIndex > buffer.Length - sizeof(double))
                ThrowArgumentException();

            GetBytesFromDouble(value, isLittleEndian).AsSpan().CopyTo(buffer.AsSpan().Slice(startIndex, sizeof(double)));
            return true;
        }

        #endregion
    }
}
